import { useMemo, useState } from "react";
import { useAtomValue } from "jotai";
import { Button, InputNumber, Radio, Switch } from "antd";
import Chart from "../Chart";
import ProgramSelectModal from "./ProgramSelectModal";
import AddIcon from "@/assets/images/add.png?jsx";
import FullSizeModal from "../FullSizeModal";
import useFetch from "@/lib/hooks/useFetch";
import { getLimitWarning } from "@/lib/api/alert";
import { tokenAtom } from "@/store/user";
import ProgramStatus from "../ProgramList/ProgramStatus";
import type {
  IDeviceProgram,
  IDeviceStatus,
  ILimitWarning,
  IProgram,
  IProgramStatus,
} from "@/lib/types";
import {
  getDeviceProgram,
  updateDeviceProgram as updateDeviceProgramApi,
} from "@/lib/api/device";
import { useMessage } from "@/providers/AntdMessageProvider";
import useOnUpdated from "@/lib/hooks/useOnUpdated";
import { useDeviceContext } from "@/providers/DeviceProvider";
import clsm from "@/lib/utils/clsm";
import type { EChartsCoreOption } from "echarts";
import ProgramSummary from "../ProgramList/ProgramSummary";

type ProgramSettingDialogProps = {
  visible: boolean;
  onVisibleChange: (v: boolean) => void;
};
export default function ProgramSettingDialog(props: ProgramSettingDialogProps) {
  const { visible, onVisibleChange } = props;
  const [view, setView] = useState<"table" | "chart">("table");
  const [programSelectModalVisible, setProgramSelectModalVisible] =
    useState(false);
  const [warningLimits, setWarningLimit] = useState<Partial<ILimitWarning>>({
    hWarningLimitLower: 0,
    hWarningLimitUpper: 0,
    pWarningLimitLower: 0,
    pWarningLimitUpper: 0,
    tWarningLimitLower: 0,
    tWarningLimitUpper: 0,
  });
  const [controls, setControls] = useState({
    isLimitWarning: "0" as "0" | "1",
    isMeasureWarning: "0" as "0" | "1",
  });
  const [program, setProgram] = useState<IProgram>();
  const [status, setStatus] = useState<IProgramStatus[]>();
  const [programChanged, setProgramChanged] = useState(false);

  const token = useAtomValue(tokenAtom);
  const { device } = useDeviceContext();

  const {
    isLoading,
    refetch: loadDeviceProgram,
    cancel: cancelLoadDeviceProgram,
  } = useFetch({
    fetchFn: (id) =>
      getDeviceProgram(id, token).then((res) => {
        if (res.code !== 200) {
          throw res;
        }
        return res.data;
      }),
    initialPayload: device!.deviceId,
    onSuccess(data) {
      setWarningLimit({
        hWarningLimitLower: data.hWarningLimitLower,
        hWarningLimitUpper: data.hWarningLimitUpper,
        pWarningLimitLower: data.pWarningLimitLower,
        pWarningLimitUpper: data.pWarningLimitUpper,
        tWarningLimitLower: data.tWarningLimitLower,
        tWarningLimitUpper: data.tWarningLimitUpper,
      });
      setProgram({
        loop: data.loop,
        programName: data.programName,
        programId: data.programId,
        remark: data.remark,
      } as IProgram);
      setStatus(data.programData);
      setControls({
        isLimitWarning: data.isLimitWarning,
        isMeasureWarning: data.isMeasureWarning,
      });
    },
  });

  const {
    isLoading: isLoadingLimitWarning,
    refetch: loadLimits,
    cancel: cancelLoadLimits,
  } = useFetch({
    fetchFn: () =>
      getLimitWarning(token).then((res) => {
        if (res.code !== 200) {
          throw res;
        }
        return res.data;
      }),
    onSuccess(data) {
      const newData = {} as typeof warningLimits;
      for (const k in warningLimits) {
        const key = k as keyof typeof newData;
        newData[key] = data[key] as any;
      }
      setWarningLimit(newData);
    },
  });

  function handleSelectProgram(program: IProgram, status: IProgramStatus[]) {
    setProgram((p) => {
      setProgramChanged(p?.programId !== program.programId);
      return program;
    });
    setStatus(status);
    setControls({ isLimitWarning: "1", isMeasureWarning: "0" });
    loadLimits();
  }

  useOnUpdated(() => {
    if (visible) {
      loadLimits();
      loadDeviceProgram(device!.deviceId);
    }
    return () => {
      cancelLoadLimits();
      cancelLoadDeviceProgram();
    };
  }, [visible, device!.deviceId]);

  return (
    <>
      <FullSizeModal
        visible={visible}
        onClose={() => onVisibleChange(false)}
        title="程序设置"
        fullscreen
      >
        <div className="flex h-full flex-col px-8">
          <div className="flex min-h-0 flex-auto flex-col">
            <div className="my-4 text-center text-sm">
              <Radio.Group
                defaultValue={view}
                value={view}
                onChange={(e) => setView(e.target.value)}
              >
                <Radio.Button value="table">列表</Radio.Button>
                <Radio.Button value="chart">曲线</Radio.Button>
              </Radio.Group>
            </div>
            <div className="min-h-0 flex-auto">
              <TableView
                visible={view === "table"}
                device={device!}
                program={program}
                programStatus={status}
                loading={isLoading}
                limits={warningLimits}
                controls={controls}
                onLimitsChange={setWarningLimit}
                onControlsChange={setControls}
                onOpenProgramSelectModal={() =>
                  setProgramSelectModalVisible(true)
                }
                programChanged={programChanged}
              />
              <ChartView visible={view === "chart"} status={status} />
            </div>
          </div>
          <ProgramSummary program={program} status={status} />
        </div>
      </FullSizeModal>
      <ProgramSelectModal
        visible={programSelectModalVisible}
        onClose={() => setProgramSelectModalVisible(false)}
        onConfirmSelection={handleSelectProgram}
      />
    </>
  );
}

type TableViewProps = {
  visible: boolean;
  device: IDeviceStatus;
  limits: Partial<ILimitWarning>;
  controls: { isLimitWarning: "0" | "1"; isMeasureWarning: "0" | "1" };
  loading: boolean;
  programChanged: boolean;
  program?: IProgram;
  programStatus?: IProgramStatus[];
  onLimitsChange: (
    update:
      | Partial<ILimitWarning>
      | ((old: Partial<ILimitWarning>) => Partial<ILimitWarning>),
  ) => void;
  onControlsChange: (
    update:
      | {
          isLimitWarning: "0" | "1";
          isMeasureWarning: "0" | "1";
        }
      | ((old: { isLimitWarning: "0" | "1"; isMeasureWarning: "0" | "1" }) => {
          isLimitWarning: "0" | "1";
          isMeasureWarning: "0" | "1";
        }),
  ) => void;
  onOpenProgramSelectModal: () => void;
};
function TableView(props: TableViewProps) {
  const {
    visible,
    device,
    loading,
    program,
    limits,
    controls,
    programChanged,
    programStatus,
    onLimitsChange,
    onControlsChange,
    onOpenProgramSelectModal,
  } = props;

  const token = useAtomValue(tokenAtom);
  const message = useMessage();

  const { isLoading: isSaving, refetch: updateDeviceProgram } = useFetch({
    fetchFn: (data: Partial<IDeviceProgram>) =>
      updateDeviceProgramApi(data, token).then((res) => {
        if (res.code !== 200) {
          throw res;
        }
      }),
    onSuccess() {
      message.success("保存成功");
    },
    onError(error) {
      let _message = "保存失败";
      if ("msg" in error) {
        _message = error.msg;
      } else if ("message" in error) {
        _message = error.message;
      }
      message.success(_message);
    },
  });

  function handleSave() {
    if (programChanged && device.deviceStatus !== "1") {
      message.error("请先停机！");
      return;
    }
    updateDeviceProgram({
      deviceId: device.deviceId,
      programId: program!.programId,
      loop: program!.loop,
      ...limits,
      ...controls,
      programData: programStatus,
    });
  }

  return (
    <>
      <div className={clsm("flex h-full flex-col", !visible && "hidden")}>
        <div className="mb-4 flex min-h-0 flex-none justify-evenly gap-x-4">
          <div className="grid grid-cols-2 items-center gap-4">
            {/* {device!.deviceStatus === "1" && (
              <Button
                className="col-start-1 row-start-1"
                onClick={onOpenProgramSelectModal}
              >
                <AddIcon className="absolute left-1 size-5" />
                选择
              </Button>
            )} */}
            <Button
              className="col-start-1 row-start-2"
              onClick={handleSave}
              loading={isSaving}
            >
              <AddIcon className="absolute left-1 size-5" />
              保存
            </Button>
            <div className="col-start-2 row-start-1">
              控制点报警&nbsp;
              <Switch
                value={controls.isLimitWarning === "1"}
                onChange={(v) =>
                  onControlsChange((p) => ({
                    ...p,
                    isLimitWarning: v ? "1" : "0",
                  }))
                }
              />
            </div>
            <div className="col-start-2 row-start-2">
              测量点报警&nbsp;
              <Switch
                value={controls.isMeasureWarning === "1"}
                onChange={(v) =>
                  onControlsChange((p) => ({
                    ...p,
                    isMeasureWarning: v ? "1" : "0",
                  }))
                }
              />
            </div>
          </div>
          <div className="flex gap-4 *:basis-1/3">
            {device!.isTemperature === 1 && (
              <div className="*:flex">
                <div className="mb-4">
                  <span className="mr-2 whitespace-nowrap">温度超调(上限)</span>
                  <InputNumber
                    value={limits.tWarningLimitUpper}
                    onChange={(v) =>
                      onLimitsChange((p) => ({
                        ...p,
                        tWarningLimitUpper: v || undefined,
                      }))
                    }
                  />
                </div>
                <div>
                  <span className="mr-2 whitespace-nowrap">温度超调(下限)</span>
                  <InputNumber
                    value={limits.tWarningLimitLower}
                    onChange={(v) =>
                      onLimitsChange((p) => ({
                        ...p,
                        tWarningLimitLower: v || undefined,
                      }))
                    }
                  />
                </div>
              </div>
            )}
            {device!.isHumidity === 1 && (
              <div className="*:flex">
                <div className="mb-4">
                  <span className="mr-2 whitespace-nowrap">湿度超调(上限)</span>
                  <InputNumber
                    value={limits.hWarningLimitUpper}
                    onChange={(v) =>
                      onLimitsChange((p) => ({
                        ...p,
                        hWarningLimitUpper: v || undefined,
                      }))
                    }
                  />
                </div>
                <div>
                  <span className="mr-2 whitespace-nowrap">湿度超调(下限)</span>
                  <InputNumber
                    value={limits.hWarningLimitLower}
                    onChange={(v) =>
                      onLimitsChange((p) => ({
                        ...p,
                        hWarningLimitLower: v || undefined,
                      }))
                    }
                  />
                </div>
              </div>
            )}
            {device!.isPressure === 1 && (
              <div className="*:flex">
                <div className="mb-4">
                  <span className="mr-2 whitespace-nowrap">压力超调(上限)</span>
                  <InputNumber
                    value={limits.pWarningLimitUpper}
                    onChange={(v) =>
                      onLimitsChange((p) => ({
                        ...p,
                        pWarningLimitUpper: v || undefined,
                      }))
                    }
                  />
                </div>
                <div>
                  <span className="mr-2 whitespace-nowrap">压力超调(下限)</span>
                  <InputNumber
                    value={limits.pWarningLimitLower}
                    onChange={(v) =>
                      onLimitsChange((p) => ({
                        ...p,
                        pWarningLimitLower: v || undefined,
                      }))
                    }
                  />
                </div>
              </div>
            )}
          </div>
        </div>
        <div className="min-h-0 flex-auto overflow-auto">
          <ProgramStatus
            loading={loading}
            data={programStatus || []}
            device={device}
            advanced
            limits={limits}
          />
        </div>
      </div>
    </>
  );
}

type ChartViewProps = {
  visible: boolean;
  status?: IProgramStatus[];
};
type CombinedDataItem = {
  label: string;
  t: number;
  h: number;
  p: number;
  tRate: string;
  hRate: string;
  pRate: string;
};
function ChartView(props: ChartViewProps) {
  const { visible, status } = props;
  const options = useMemo<EChartsCoreOption | null>(() => {
    if (!status) {
      return null;
    }
    const combinedData: CombinedDataItem[] = [];
    let i = 0;
    while (i < status.length) {
      let next = 0;
      for (let j = 0; j < status[i].segmentLoop; j++) {
        for (
          let k = i;
          k < status.length && status[k].segmentNo === status[i].segmentNo;
          k++
        ) {
          combinedData.push({
            label: `${status[k].segmentNo}-${status[k].stepNo}`,
            t: status[k].stepTSv,
            h: status[k].stepHSv,
            p: status[k].stepPSv,
            tRate: status[k].stepTIsRate,
            hRate: status[k].stepHIsRate,
            pRate: status[k].stepPIsRate,
          });
          next = Math.max(next, k + 1);
        }
      }
      i = next;
    }
    return {
      tooltip: { show: false },
      legend: {
        icon: "rect",
        textStyle: { color: "#fff" },
        data: [{ name: "温度" }, { name: "湿度" }, { name: "压力" }],
      },
      dataZoom: [
        {
          type: "inside",
          start: 0,
          end: 100,
        },
      ],
      xAxis: {
        type: "category",
        boundaryGap: false,
        data: getXAxisData(combinedData),
      },
      yAxis: [
        {
          name: "温度",
          type: "value",
          // show: false,
          splitLine: {
            lineStyle: { opacity: 0.2 },
          },
          offset: 20,
          nameTextStyle: { align: "right" },
        },
        {
          name: "湿度",
          type: "value",
          // show: false,
          position: "left",
          alignTicks: true,
          splitLine: {
            lineStyle: { opacity: 0.2 },
          },
          offset: 50,
          nameTextStyle: { align: "right" },
        },
        {
          name: "压力",
          type: "log",
          // show: false,
          position: "right",
          alignTicks: true,
          splitLine: {
            lineStyle: { opacity: 0.2 },
          },
          offset: 15,
          nameTextStyle: { align: "left", padding: [0, 0, 0, 10] },
        },
      ],
      series: [
        {
          name: "温度",
          getValue: (item: CombinedDataItem) => item.t,
          getIsRate: (item: CombinedDataItem) => item.tRate === "1",
        },
        {
          name: "湿度",
          getValue: (item: CombinedDataItem) => item.h,
          getIsRate: (item: CombinedDataItem) => item.hRate === "1",
        },
        {
          name: "压力",
          getValue: (item: CombinedDataItem) => item.p,
          getIsRate: (item: CombinedDataItem) => item.pRate === "1",
        },
      ].map(({ name, getValue, getIsRate }, index) =>
        getSeriesOption(
          name,
          index,
          getLineData(combinedData, getValue, getIsRate),
        ),
      ),
    };
  }, [status]);
  return (
    <div className={clsm("h-full", !visible && "hidden")}>
      {visible && (
        <Chart className="h-full overflow-hidden rounded" options={options} />
      )}
    </div>
  );
}

function getXAxisData(data: CombinedDataItem[]) {
  return ["start", ...data.map((item) => item.label)];
}

function getLineData(
  data: CombinedDataItem[],
  getValue: (item: CombinedDataItem) => number,
  getIsRate: (item: CombinedDataItem) => boolean,
) {
  const res: [number, number][] = [[0, getValue(data[0])]];
  for (let i = 0, x = 0; i < data.length; i++) {
    const curr = data[i];
    if (getIsRate(curr)) {
      res.push([++x, getValue(curr)]);
    } else {
      const prev = res[res.length - 1];
      if (prev[1] === getValue(curr)) {
        res.push([++x, getValue(curr)]);
      } else {
        res.push([x, getValue(curr)], [++x, getValue(curr)]);
      }
    }
  }
  return res;
}

function getYAxisOption() {}

function getSeriesOption(name: string, yAxisIndex: number, data: any) {
  return {
    type: "line",
    name,
    label: { show: true, color: "#fff" },
    emphasis: {
      focus: "series",
    },
    yAxisIndex,
    data,
  };
}
